<?php

require_once 'quickstart-loading-re.php';

function microtime_float(){
    list($utime, $time) = explode(" ", microtime());
    return ((float)$utime + (float)$time);
}

function lte($left, $right){
	return $left <= $right;
}

function gte($left, $right){
	return $left >= $right;
}

function gt($left, $right){
	return $left > $right;
}

function generate_feasible_points($tuple, $constraints, $inc = 1, $limit = 100){

	$points = re::relation(array(), array('name'=>'string', 'x'=>'double', 'y'=>'double'));

	$step = -1;
	$ct = $tuple->get('constraint_type');	
	$n  = $tuple->get('name');
	$tx = $tuple->get('x_coeff');
	$ty = $tuple->get('y_coeff');
	$tc = $tuple->get('c');
	
	if ($ct == re_NNC ){
	
		$tx = ($tx != 0) ? $tx
				 : (
					$constraints->join(
						re::relation(array(
							array('x_coeff'=>1, 'constraint_type'=>re_NNC)
						))
					)->max('c')
				 );
				 
		$ty = ($ty != 0) ? $ty
				 : (
					$constraints->join(
						re::relation(array(
							array('y_coeff'=>1, 'constraint_type'=>re_NNC)
						))
					)->max('c')
				 );
	}
	
	$c  = $constraints->minus($tuple->toRelation())->toArray();
	
	while( $limit >= $step ){
		$step = $step + $inc;
		if( $ct == re_MC ){
			$x = $step;
			$y = ($tc - ($tx * $x)) / $ty;		
		}
		else if ($ct == re_NNC ){
			$x = ($tx == 1) ? ($tx * $step) : $tx;
			$y = ($ty == 1) ? ($ty * $step) : $ty;
		}
		
		$ok = TRUE;
		foreach($c as $t){
		
			$_ct = $t->get('constraint_type');
			$_c  = $t->get('c');
			$_iq = $t->get('iq');
			$_tx = $t->get('x_coeff');
			$_ty = $t->get('y_coeff');
			
			if( $_ct == re_MC ){
				$ok = re::invokeOp($_iq, array(($x * $_tx) + ($y * $_ty), $_c));
			}
			else 	if ( $_ct == re_NNC ){
				$_a  = ($_tx == 1) ? $x : $y;
				$_b  = $_c;
				$ok  = re::invokeOp($_iq, array($_a, $_b));
			}
			
			if( !$ok ){
				break;
			}
		}
		
		if( $ok ){
			$points = $points->union(
				re::relation(array(
					array('name'=>$n, 'x'=>(double)$x, 'y'=>(double)$y)
				))
			);
		}	
	}
	
	return $points;
}

function compute_objective($tuple){
	return ($tuple->points->x * $tuple->x_coeff) + ($tuple->points->y * $tuple->y_coeff);
}

define('re_MC', 'MAIN_CONSTRAINT');
define('re_NNC', 'NONNEGATIVITY_CONSTRAINT');

$objective_fn = re::tuple(array('x_coeff'=>50, 'y_coeff'=>60));
$constraints   = re::relation(array(
	array('name'=>'1', 'x_coeff'=>1, 'y_coeff'=>2, 'iq'=>'lte', 'c'=>100, 'constraint_type'=>re_MC)
   ,array('name'=>'2', 'x_coeff'=>4, 'y_coeff'=>3, 'iq'=>'lte', 'c'=>200, 'constraint_type'=>re_MC)
   ,array('name'=>'3', 'x_coeff'=>1, 'y_coeff'=>0, 'iq'=>'gte', 'c'=>6,    'constraint_type'=>re_NNC)
   ,array('name'=>'4', 'x_coeff'=>0, 'y_coeff'=>1, 'iq'=>'gte', 'c'=>12,  'constraint_type'=>re_NNC)
));

/*
//another example

$objective_fn = re::tuple(array('x_objective'=>180, 'y_objective'=>100));
$constraints = re::relation(array(	
	array('name'=>'1', 'x_coeff'=>4, 'y_coeff'=>2, 'iq'=>'lte', 'c'=>60, 'constraint_type'=>re_MC)
   ,array('name'=>'2', 'x_coeff'=>2, 'y_coeff'=>4, 'iq'=>'lte', 'c'=>48, 'constraint_type'=>re_MC)
   ,array('name'=>'3', 'x_coeff'=>1, 'y_coeff'=>0, 'iq'=>'gte', 'c'=>0, 'constraint_type'=>re_NNC)
   ,array('name'=>'4', 'x_coeff'=>0, 'y_coeff'=>1, 'iq'=>'gte', 'c'=>0, 'constraint_type'=>re_NNC)
));

*/

$constraints = $constraints->extend(
	array(
		'POINTS'=>'generate_feasible_points'
	)
   ,array('POINTS'=>array('args'=>array($constraints, 1, 50)))
);

$feasible_points = $constraints
	->aggUnion('POINTS')
	->wrap(array('POINTS'=>array(array('x', 'y'))))
	->summarizeBy(array(array('POINTS')), array(
		'INTERSECT_COUNT'=>array('count', array('POINTS'))
	 ))
	->join(re::relation(array(array('INTERSECT_COUNT'=>2))));

$feasible_plans = $feasible_points->join($objective_fn->toRelation())->extend(array(
	'Z'=>'compute_objective'
), array(
	'Z'=>re::AS_OP_EXPR
));

$optimal_plan = $feasible_plans
		->rename(array('Z'=>'_Z'))
		->summarizeAll(array('Z'=>array('max', array('_Z'))))
		->join($feasible_plans)
		 ->project(array('INTERSECT_COUNT'), TRUE);
	
print_r($optimal_plan->_sr(true));